using System;

namespace Bookvar.ObjectModel.Actions
{
    /// <summary>
    /// A base class for actions.
    /// </summary>
    public abstract class ActionBase : IAction
    {
        /// <summary>
        /// Gets a value indicating the state of the action. The action is <see cref="ActionState.New"/> 
        /// when it is newly created. An action is <see cref="ActionState.Executed"/> after <see cref="Execute"/> 
        /// is called. And after <see cref="Undo"/> is called the action is <see cref="ActionState.Undone"/>.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance is executed; otherwise, <c>false</c>.
        /// </value>
        public ActionState State { get; protected set; }

        #region IAction Members

        /// <summary>
        /// Executes (or redoes) the logic for the action.
        /// </summary>
        /// <exception cref="InvalidOperationException">if the action is already executed.</exception>
        public virtual void Execute()
        {
            if(State == ActionState.Executed)
            {
                throw new InvalidOperationException( "The action is already executed." );
            }

            State = ActionState.Executed;
        }

        /// <summary>
        /// Undoes the action.
        /// </summary>
        /// <exception cref="InvalidOperationException">if called before the action is executed.</exception>
        public virtual void Undo()
        {
            if ( State == ActionState.Undone || State == ActionState.New )
            {
                throw new InvalidOperationException( "The action should be executed first before you can undo it." );
            }

            State = ActionState.Undone;
        }

        /// <summary>
        /// Gets a value indicating whether this action supports redo. If not overridden this is by default true.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this action supports redo.; otherwise, <c>false</c>.
        /// </value>
        public virtual bool CanRedo
        {
            get { return true; }
        }

        /// <summary>
        /// Gets a value indicating whether this action supports undo. If not overridden this is by default true.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if his action supports undo; otherwise, <c>false</c>.
        /// </value>
        public virtual bool CanUndo
        {
            get { return true; }
        }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            Dispose( true );
            GC.SuppressFinalize( this );
        }

        #endregion

        /// <summary>
        /// Calls <see cref="Dispose(bool)"/> method with <c>false</c>. If you need to clean any unmanaged resources 
        /// override the dispose method.
        /// </summary>
        /// <remarks>
        /// This is an implementation of the Dispose pattern - for more info ask google.
        /// </remarks>
        ~ActionBase()
        {
            Dispose( false );
        }

        /// <summary>
        /// Releases unmanaged and - optionally - managed resources
        /// </summary>
        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; 
        /// <c>false</c> to release only unmanaged resources.
        /// </param>
        protected virtual void Dispose( bool disposing )
        {
        }
    }
}